package com.zh.webcommon.common.support;

import com.zh.webcommon.common.exception.NotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;

/**
 * @author ZH
 * @date 11:45 2023/3/27
 */
@Slf4j
public final class RedisCacheHelper implements DisposableBean {
    private static final String CACHE_KEY_PREFIX = "global_cache:%s";
    private static RedisTemplate<String, Object> redisTemplate;
    /**
     * 缓存过期时间（分钟）
     */
    private static int expireMinutes;

    public RedisCacheHelper(RedisTemplate<String, Object> redisTemplate, int expireMinutes) {
        RedisCacheHelper.redisTemplate = redisTemplate;
        RedisCacheHelper.expireMinutes = expireMinutes;
    }

    @Override
    public void destroy() throws Exception {
        redisTemplate = null;
    }

    /**
     * 缓存查询的数据
     *
     * @param key    定义一个缓存的key
     * @param fun    要缓存的方法
     * @param backup 若方法执行失败时的备用数据
     * @param <T> T
     * @return 数据
     */
    public static <T> T cacheFun(String key, Supplier<T> fun, Supplier<T> backup) {
        T obj = getCacheRedis(key);
        if (obj == null) {
            try {
                obj = fun.get();
            } catch (NotFoundException ex) {
                log.error(ex.getMessage());
                if (backup != null)
                    obj = backup.get();
                else
                    throw ex;
            } finally {
                if (obj != null)
                    cacheRedisIfAbsent(key, obj);
            }
        }
        return obj;
    }

    /**
     * 缓存数据
     */
    public static <T> boolean cacheRedis(String key, T value) {
        return cacheRedis(key, value, Duration.ofMinutes(expireMinutes));
    }

    /**
     * 缓存数据
     */
    public static <T> boolean cacheRedis(String key, T value, Duration expire) {
        try {
            redisTemplate.opsForValue().set(getRedisKey(key), value, expire);
            return true;
        } catch (Exception ex) {
            log.error("[cacheRedis] cache key[{}] 失败。cause：{}", key, ex.getMessage());
            return false;
        }
    }


    /**
     * 缓存数据
     */
    public static <T> boolean cacheRedisIfAbsent(String key, T value) {
        try {
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(getRedisKey(key), value, Duration.ofMinutes(expireMinutes));
            return Boolean.TRUE.equals(flag);
        } catch (Exception ex) {
            log.error("[cacheRedis] cache key[{}] 失败。cause：{}", key, ex.getMessage());
            return false;
        }
    }

    /**
     * 删除缓存数据
     */
    public static boolean removeCacheRedis(String... key) {
        List<String> keys = Arrays.stream(key).filter(StringUtils::hasText)
                .map(RedisCacheHelper::getRedisKey).toList();
        try {
            Long i = redisTemplate.delete(keys);
            return i != null && i > 0;
        } catch (Exception ex) {
            log.error("[cacheRedis] remove key[{}] 失败。cause：{}", key, ex.getMessage());
            return false;
        }
    }

    /**
     * 模糊删除缓存数据
     */
    public static boolean removeCacheRedisPattern(String pattern) {
        Set<String> keys = redisTemplate.keys(String.format(CACHE_KEY_PREFIX, pattern));
        if (CollectionUtils.isEmpty(keys)) return false;

        try {
            Long i = redisTemplate.delete(keys);
            return i != null && i > 0;
        } catch (Exception ex) {
            log.error("[cacheRedis] remove key[{}] 失败。cause：{}", String.format(CACHE_KEY_PREFIX, pattern), ex.getMessage());
            return false;
        }
    }

    /**
     * 获取缓存数据
     */
    @SuppressWarnings("unchecked")
    public static <T> T getCacheRedis(String key) {
        try {
            return (T) redisTemplate.opsForValue().get(getRedisKey(key));
        } catch (Exception ex) {
            log.error("[cacheRedis] getCache key[{}] 失败。cause：{}", key, ex.getMessage());
            return null;
        }
    }

    private static String getRedisKey(String key) {
        return String.format(CACHE_KEY_PREFIX, key);
    }

}
